package edu.uow.scsse.main.transformation;

import java.util.ArrayList;
import java.util.List;

//import org.apache.log4j.Logger;


import edu.uow.scsse.main.domain.Edge;
import edu.uow.scsse.main.domain.EdgeType;
import edu.uow.scsse.main.domain.Graph;
import edu.uow.scsse.main.domain.Node;
import edu.uow.scsse.main.domain.NodeType;
/**
 * Class for Transform the file content list into Graph
 * @author Tiancheng Zhang
 *
 */
public class Transformation {

//	private static final Logger logger = Logger.getLogger(Transformation.class);
	
	/**asl file reader*/
	private ASLFileReader aslFileReader;
	
	/**List of Graphs*/
	private List<String> fileContent;
	private Graph domainGraph;
	boolean []processed;
	private List<Graph> graphList;
	
	/**
	 * Transformation Constructor
	 */
	public Transformation(String filePath)
	{
		aslFileReader = new ASLFileReader(filePath);
		domainGraph = new Graph();
		graphList = new ArrayList<Graph>();
	}
	
	/**
	 *Generate the Graph
	 */
	public List<Graph> generate() throws Exception
	{
		//read the file
		aslFileReader.readFile();
		fileContent = aslFileReader.getFileContent();
		
		processed = new boolean[fileContent.size()];
		//initialization
		for(int i = 0; i < fileContent.size(); i++)
		{
			processed[i] = false;
		}
		

			
		int triEventIndex = findNextEntry();
		
		//has trigging event
		while(triEventIndex != -1)
		{
//			logger.info("Start of Create Graph For Trigging event:"+ fileContent.get(triEventIndex));
			//mark the TE as processed
//			processed[triEventIndex] = true;
			domainGraph = new Graph();
			
			//Create a start node
			Node startNode = new Node(-1, fileContent.get(triEventIndex), NodeType.TRIGGINGEVENT, false, new ArrayList<Edge>(), new ArrayList<Edge>());
			domainGraph.getNodes().add(startNode);
//			logger.info("TR EVENT: "+fileContent.get(triEventIndex)+" is found");
			
			eachTEDomainGraphGeneration(triEventIndex, -1);
			
			graphList.add(domainGraph);
			
//			logger.info("End of Create Graph For Trigging event:"+ fileContent.get(triEventIndex));
			triEventIndex = findNextEntry();
		}
		return graphList;
	}
	
	/**
	 * Generate graph for each trigging event
	 * @param entry trigging event index
	 */
	private void eachTEDomainGraphGeneration(int entry, int prevNodeId) throws Exception
	{
		//for each plan
		do
		{
			processed[entry] = true;
			//consider entry type
			Node planNode = new Node(entry, fileContent.get(entry), NodeType.PLAN, false, new ArrayList<Edge>(), new ArrayList<Edge>());
			//setup the link
			if(this.domainGraph.findNodeById(prevNodeId) != null)
			{
				this.domainGraph.findNodeById(prevNodeId).getOutgoingEdges().add(new Edge(prevNodeId,entry, EdgeType.ALTPLANEDGE));
				planNode.getInComingEdges().add(new Edge(prevNodeId,entry, EdgeType.ALTPLANEDGE));
			}
			else
			{
//				logger.info("Cannot Find the previous Node with id:" + prevNodeId);
				throw new Exception("Cannot Find the Node with id:"+prevNodeId);
			}
			//add plan Node to Graph
			this.domainGraph.getNodes().add(planNode);
//			logger.info("PLAN Node: "+domainGraph.findNodeById(entry).getName()+" added");
			//traverse the plan
			for(int i = entry + 1; i < fileContent.size(); i++)
			{
				//is not process && is (action/subgoal/context condition)
				if(processed[i] == false && (fileContent.get(i).charAt(0) == '<' || fileContent.get(i).charAt(0) == ':'))
				{
					if(fileContent.get(i).contains("!")&&fileContent.get(i).charAt(0) == '<')//subgoal
					{
						Node subgoalNode = new Node(i, fileContent.get(i).substring(2), NodeType.SUBGOAL,
													false, new ArrayList<Edge>(), new ArrayList<Edge>());
						//previous node index i - 1, set up link
						if(domainGraph.findNodeById(i-1) != null)
						{
							domainGraph.findNodeById(i-1).getOutgoingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
							subgoalNode.getInComingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
						}
						else
						{
//							logger.info("Cannot Find the previous Node with id:" + (i-1));
							throw new Exception("Cannot Find the Node with id:"+(i-1));
						}
						domainGraph.getNodes().add(subgoalNode);
						this.processed[i] = true;
//						logger.info("SUBGOAL Node: "+domainGraph.findNodeById(i).getName()+" added");
						//recursion
						while(getAlternativePlan(fileContent.get(i).substring(2)) != -1)
						{
							eachTEDomainGraphGeneration(getAlternativePlan(fileContent.get(i).substring(2)), i);
						}
						
						
					}
					else if(fileContent.get(i).charAt(0) == '<')//action
					{	
						Node actionNode = new Node(i, fileContent.get(i).substring(2), NodeType.ACTION,
												   false, new ArrayList<Edge>(), new ArrayList<Edge>());
						//previous node index i - 1, set up link
						if(domainGraph.findNodeById(i-1) != null)
						{
							domainGraph.findNodeById(i-1).getOutgoingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
							actionNode.getInComingEdges().add(new Edge(i-1, i , EdgeType.SEQUENCIALEDGE));
						}
						else
						{
//							logger.info("Cannot Find the previous Node with id:" + (i-1));
							throw new Exception("Cannot Find the Node with id:"+(i-1));
						}
						domainGraph.getNodes().add(actionNode);
						this.processed[i] = true;
//						logger.info("ACTION Node: "+domainGraph.findNodeById(i).getName()+" added");
					}
					else if(fileContent.get(i).charAt(0) == ':')
					{
						Node contextConNode = new Node(i, fileContent.get(i).substring(1), NodeType.CONTEXT_CONDITION,
													   false, new ArrayList<Edge>(), new ArrayList<Edge>());
						//previous node index i - 1, set up link
						if(domainGraph.findNodeById(i-1) != null)
						{
							domainGraph.findNodeById(i-1).getOutgoingEdges().add(new Edge(i-1, i, EdgeType.SEQUENCIALEDGE));
							contextConNode.getInComingEdges().add(new Edge(i-1, i , EdgeType.SEQUENCIALEDGE));
						}
						else
						{
//							logger.info("Cannot Find the previous Node with id:" + (i-1));
							throw new Exception("Cannot Find the Node with id:"+(i-1));
						}
						domainGraph.getNodes().add(contextConNode);
						this.processed[i] = true;
//						logger.info("CONTECT CON Node: "+domainGraph.findNodeById(i).getName()+" added");
					}//end else
				}
				else
				{
					break;
				}//end else
				
			  }//end for
		}while((entry = getAlternativePlan(fileContent.get(entry))) != -1);
	}
	
	/**
	 * Find the entry
	 * @return index (-1 not found)
	 */
	private int findNextEntry()
	{
		int index = -1;
		for(int i = 0; i < fileContent.size(); i++)
		{
			//an trigging event that has not been processed
			if(fileContent.get(i).charAt(0) == '+' && processed[i] == false)
			{
				int j = 0;
				for(j = 0; j < fileContent.size(); j++ )
				{
					//actions. Check whether the possible trigging event exists in it
					if(fileContent.get(j).charAt(0) == '<')
					{
						if(fileContent.get(j).contains(fileContent.get(i).substring(1)))
						{
							break;
						}
					}
				}
				//found trigging event
				if(j >= fileContent.size())
				{
					index = i;
					return index;
				}	
			}
		}
		return index;
	}
	
	/**
	 * Alternative plan
	 */
	private int getAlternativePlan(String plan)
	{
		int index = -1;
		for(int i = 0; i < fileContent.size(); i++)
		{
			if(processed[i] == false)
			{
				if(fileContent.get(i).contains(plan))
				{
					index = i;
					return index;
				}
			}
		}
		return index;
	}
	
}
